"""
PETSc-Only Builder Implementation.

This builder compiles pure PETSc solver prototypes.
"""

import os
import subprocess
import time
import logging
from pathlib import Path
from typing import Dict, List, Optional, Any

from gcr_solver_manager.extensions.base_builder import (
    BuilderInterface, BuildConfig, BuildResult, BuildDependencyResult
)

logger = logging.getLogger(__name__)


class PETScOnlyBuilder(BuilderInterface):
    """Builder for PETSc-Only framework."""
    
    def __init__(self, source_dir: Path):
        """Initialize builder with source directory."""
        self.source_dir = source_dir
        self.makefile_path = source_dir / "Makefile"
        
    def check_dependencies(self) -> BuildDependencyResult:
        """Check if required dependencies are available."""
        missing_deps = []
        version_info = {}
        
        try:
            # Check for PETSc installation
            petsc_dir = os.environ.get('PETSC_DIR')
            petsc_arch = os.environ.get('PETSC_ARCH')
            
            if not petsc_dir:
                missing_deps.append('PETSC_DIR')
            else:
                version_info['petsc_dir'] = petsc_dir
                
                # Try to get PETSc version
                try:
                    petsc_version_file = Path(petsc_dir) / "include" / "petscversion.h"
                    if petsc_version_file.exists():
                        content = petsc_version_file.read_text()
                        # Extract version from petscversion.h
                        for line in content.split('\n'):
                            if '#define PETSC_VERSION_RELEASE' in line:
                                version_info['petsc_version'] = line.split()[-1]
                                break
                except Exception:
                    version_info['petsc_version'] = 'unknown'
            
            if petsc_arch:
                version_info['petsc_arch'] = petsc_arch
            
            # Check for MPI
            result = subprocess.run(['mpicc', '--version'], capture_output=True, text=True, timeout=10)
            if result.returncode == 0:
                version_info['mpi'] = result.stdout.split('\n')[0]
            else:
                missing_deps.append('mpi')
            
            # Check for make
            result = subprocess.run(['make', '--version'], capture_output=True, text=True, timeout=10)
            if result.returncode == 0:
                version_info['make'] = result.stdout.split('\n')[0]
            else:
                missing_deps.append('make')
            
            # Check if source files exist
            required_files = ['petsc_gcr_prototype.c', 'helmholtz_to_petsc.c']
            for filename in required_files:
                if not (self.source_dir / filename).exists():
                    missing_deps.append(f'source_file_{filename}')
            
            satisfied = len(missing_deps) == 0
            
            return BuildDependencyResult(
                satisfied=satisfied,
                missing_dependencies=missing_deps,
                version_info=version_info,
                error_message=None if satisfied else f"Missing dependencies: {', '.join(missing_deps)}"
            )
            
        except Exception as e:
            return BuildDependencyResult(
                satisfied=False,
                missing_dependencies=['unknown'],
                version_info={},
                error_message=f"Dependency check failed: {e}"
            )
    
    def build(self, config: BuildConfig) -> BuildResult:
        """Perform framework-specific build operations."""
        start_time = time.time()
        
        try:
            # Set up environment
            env = os.environ.copy()
            
            # Ensure PETSc environment variables are set
            if 'PETSC_DIR' not in env:
                logger.error("PETSC_DIR environment variable not set")
                return BuildResult(
                    success=False,
                    binary_path=None,
                    build_log="PETSC_DIR not set",
                    duration=time.time() - start_time,
                    error_message="PETSC_DIR environment variable required"
                )
            
            # Clean build if requested
            if config.clean_build:
                self._clean_build()
            
            # Build command - use make with PETSc
            cmd = ['make']
            
            # Add parallel jobs
            if config.parallel_jobs > 1:
                cmd.extend(['-j', str(config.parallel_jobs)])
            
            # Add debug flags
            if config.debug_mode:
                env['CFLAGS'] = env.get('CFLAGS', '') + ' -g -O0'
            else:
                env['CFLAGS'] = env.get('CFLAGS', '') + ' -O2'
            
            # Target specific executables
            targets = ['petsc_gcr_prototype', 'benchmark_petsc']
            
            all_output = []
            all_binaries = []
            
            # Build each target
            for target in targets:
                logger.info(f"Building PETSc target: {target}")
                target_cmd = cmd + [target]
                
                result = subprocess.run(
                    target_cmd,
                    cwd=self.source_dir,
                    capture_output=True,
                    text=True,
                    env=env,
                    timeout=600  # 10 minutes timeout per target
                )
                
                all_output.append(f"Building {target}:\nCommand: {' '.join(target_cmd)}\nSTDOUT:\n{result.stdout}\nSTDERR:\n{result.stderr}")
                
                if result.returncode == 0:
                    # Check if binary was created
                    binary_path = self.source_dir / target
                    if binary_path.exists():
                        all_binaries.append(binary_path)
                        logger.info(f"Successfully built {target}")
                    else:
                        logger.warning(f"Build succeeded but binary not found: {binary_path}")
                else:
                    logger.error(f"Failed to build {target}")
                    
            duration = time.time() - start_time
            
            # Determine primary binary (prefer petsc_gcr_prototype)
            primary_binary = None
            for binary in all_binaries:
                if 'petsc_gcr_prototype' in binary.name:
                    primary_binary = binary
                    break
            if not primary_binary and all_binaries:
                primary_binary = all_binaries[0]
                
            success = len(all_binaries) > 0
            
            return BuildResult(
                success=success,
                binary_path=primary_binary,
                build_log='\n\n'.join(all_output),
                duration=duration,
                error_message=None if success else "No binaries built successfully",
                cmake_args=[],  # Not using CMake for PETSc prototypes
                make_args=cmd[1:]  # Arguments passed to make
            )
            
        except subprocess.TimeoutExpired:
            return BuildResult(
                success=False,
                binary_path=None,
                build_log="Build timed out",
                duration=time.time() - start_time,
                error_message="Build timeout"
            )
        except Exception as e:
            return BuildResult(
                success=False,
                binary_path=None,
                build_log=f"Build failed with exception: {e}",
                duration=time.time() - start_time,
                error_message=str(e)
            )
    
    def clean(self, config: BuildConfig) -> bool:
        """Clean build artifacts."""
        return self._clean_build()
    
    def _clean_build(self) -> bool:
        """Clean PETSc build artifacts."""
        try:
            # Use make clean if Makefile supports it
            if self.makefile_path.exists():
                result = subprocess.run(
                    ['make', 'clean'],
                    cwd=self.source_dir,
                    capture_output=True,
                    text=True,
                    timeout=60
                )
                if result.returncode == 0:
                    logger.info("Clean completed using 'make clean'")
                    return True
            
            # Manual cleanup of common build artifacts
            patterns = [
                '*.o', '*.a', '*.so', 
                'petsc_gcr_prototype', 'benchmark_petsc',
                '*.dSYM'  # macOS debug symbols
            ]
            
            cleaned = False
            for pattern in patterns:
                for file in self.source_dir.glob(pattern):
                    if file.is_file():
                        file.unlink()
                        cleaned = True
                    elif file.is_dir():
                        import shutil
                        shutil.rmtree(file)
                        cleaned = True
            
            if cleaned:
                logger.info("Manual cleanup completed")
            
            return True
            
        except Exception as e:
            logger.error(f"Clean failed: {e}")
            return False
    
    def get_binary_path(self, config: BuildConfig) -> Optional[Path]:
        """Get path to built binary."""
        # Check for primary executable
        primary_binary = self.source_dir / "petsc_gcr_prototype"
        if primary_binary.exists():
            return primary_binary
        
        # Check for benchmark binary
        benchmark_binary = self.source_dir / "benchmark_petsc"
        if benchmark_binary.exists():
            return benchmark_binary
        
        return None
    
    def install_dependencies(self) -> bool:
        """Install missing dependencies (not implemented)."""
        logger.warning("Dependency installation not implemented for PETSc extension")
        return False